package com.zhiwen.util;

import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * 这些方法只适合单redis服务，如果想要对redis集群设置锁，需要更加复杂的方式
 *
 * @Auther: wuzhiwen
 * @Date: 2018/11/7 16:34
 * @Description:
 */
public class JedisDistributLockUtil {
    private static final String LOCK_SUCCESS = "OK";
    // 这个参数我们填的是NX，
    // 意思是SET IF NOT EXIST，
    // 即当key不存在时，我们进行set操作；
    // 若key已经存在，则不做任何操作；相当于setnx()
    private static final String SET_IF_NOT_EXIST = "NX";
    //设置超时时间
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    //设置返回成功常量
    private static final Long RELEASE_SUCCESS = 1L;
    //设置存活时间
    private static final int EXIT_TIME = 3 * 1000;
    //默认睡眠时间
    private static final int DEFAULT_SLEEP_TIME = 1000;
    //默认每次循环时间减少量
    private static final int DEFAULT_BLOCK_LOOP = 100;

    private static final Jedis jedis = new Jedis("192.168.91.129", 6379);


    /**
     * requestId可以设置为client的唯一标识可用UUID.randomUUID().toString()生成
     * 设置锁,setnx()需要设置存在时间
     *
     * @param lockKey
     * @param requestId
     */
    public static boolean setNXLock(String lockKey, String requestId) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, EXIT_TIME);
        if (result != null && result.equals(LOCK_SUCCESS)) {
            return true;
        }
        return false;
    }

    /**
     * 使用lua脚本实现 删除del
     *
     * @param lockKey
     * @param requestId
     * @return
     */
    public synchronized static boolean releaseLock(String lockKey, String requestId) {
        //lua语言。保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    /**
     * 这样写有一个缺陷是有可能会导致线程饥饿
     * 所以我们需要通过指定一个时间循环
     * 而且还会让CPU 一直消耗
     *
     * @param lockKey
     * @param requestId
     * @return
     */
    public synchronized static boolean lock(String lockKey, String requestId) {
        while (true) {
            if (setNXLock(lockKey, requestId)) {
                return true;
            }
        }
    }

    /**
     * blockTime建议根据实际方法运算时间设置
     *
     * @param lockKey
     * @param requestId
     * @param blockTime
     * @return
     * @throws InterruptedException
     */
    public synchronized static boolean lock(String lockKey, String requestId, int blockTime) throws InterruptedException {
        while (blockTime > 0) {
            if (setNXLock(lockKey, requestId)) {
                return true;
            }
            //随机产生睡眠时间，减少并发访问
            Thread.sleep((long) (Math.random() * DEFAULT_SLEEP_TIME));
            blockTime -= DEFAULT_BLOCK_LOOP;
        }
        return false;
    }


}
